﻿#include <Render\Render_Win32\Render_Win32_D3D\RENDER_WIN32_D3D9.HPP>
#include <Sys\PROJECTIONMAKER.HPP>
#include <Sys\RENDERUNITREG.HPP>
#include <MEMSTORAGE.HPP>

#include <d3d9.h>
#include <d3dx9.h>



using namespace CxxlMan;
using namespace Cxxl3D;


extern "C" CXXL_DLLEXPORT
cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID_Arg, void *Arg, ISpirit *spirit);

// 元件介面的識別碼
#define CRender_Win32_D3d9_ClassID L"1A5F7D17-30E7-45f5-BA2F-B1A43FF86819"



// 自動註冊和註銷 DatagramDisposal
class AutoRegDatagramDisposal
{
public:
  // Constructor
  AutoRegDatagramDisposal()
  {
    RenderUnit_Reg(UNITEXTNAME, IDatagramDisposal_Win32_D3d9_Group);
  }

  // Destructor
  ~AutoRegDatagramDisposal()
  {
    cxxlCM_ElementDelGroup(IDatagramDisposal_Win32_D3d9_Group);
  }
}o_AutoRegDatagramDisposal;

// 存放渲染 D3d 的 Datagram Disposal 物件
class RenderDatagramDisposalCenter:public IRenderDatagramDisposalCenterD3d,
  public IDatagramDisposal // 為了獲得橋接能力
{
  // 這是 pool
  class DatagramDisposalTree:public cxxlAVL_Tree<IDatagramDisposal_D3d9, UNICODE_String, true>
  {
    bool cxxlFASTCALL isLeftAt(UNICODE_String O_Key, UNICODE_String addKey) const
    {
      return O_Key <= addKey;
    }

    bool cxxlFASTCALL isEqual(UNICODE_String O_Key, UNICODE_String findKey)  const
    {
      return O_Key == findKey;
    } 

  public:
    DatagramDisposalTree(ISpirit *spirit = Spirit_Easy)  // Constructor
     :cxxlObject(spirit)
    {
    }

  }m_DatagramDisposalTree;

  // 取得 ID 指定的 DD_D3d9
  virtual CxxlMan::Smart_Ptr<IDatagramDisposal_D3d9> cxxlFASTCALL GetIDatagramDisposal_D3d9(CxxlMan::UNICODE_String ID)
  {
    Smart_Ptr<IDatagramDisposal_D3d9> IDatagramDisposal_D3d9_Ptr = m_DatagramDisposalTree.GetObj(ID); // 先嘗試由 pool 取得

    if(IDatagramDisposal_D3d9_Ptr.isNULL())
    {
      IDatagramDisposal_D3d9_Ptr = Smart_Cast<IDatagramDisposal_D3d9>( cxxlCM_GetElement(ID, UNICODE_String(IDatagramDisposal_Win32_D3d9_Group) ) );
      if(IDatagramDisposal_D3d9_Ptr.isNULL() == false)
        m_DatagramDisposalTree.Add(IDatagramDisposal_D3d9_Ptr, ID);  // 放入 pool 中
    } 

    return IDatagramDisposal_D3d9_Ptr;
  }


public:
  // Constructor
  RenderDatagramDisposalCenter()
    :cxxlObject(Spirit_Easy)
  {}

  // Destructor
  virtual ~RenderDatagramDisposalCenter()
  {}

  // 執行渲染，若 IRenderDatagram_Arg 對應的 IDatagramDisposal 不存在回覆 false
  bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, LPDIRECT3DDEVICE9 pDEVICE9)
  {
    UNICODE_String RenderDatagramID = GetRenderDatagramID(IRenderDatagram_Arg);
    Smart_Ptr<IDatagramDisposal_D3d9> IDatagramDisposal_D3d9_Ptr = m_DatagramDisposalTree.GetObj(RenderDatagramID); // 先嘗試由 pool 取得

    if(IDatagramDisposal_D3d9_Ptr.isNULL())
    {
      IDatagramDisposal_D3d9_Ptr = Smart_Cast<IDatagramDisposal_D3d9>( cxxlCM_GetElement(RenderDatagramID, UNICODE_String(IDatagramDisposal_Win32_D3d9_Group) ) );
      if(IDatagramDisposal_D3d9_Ptr.isNULL())
        return false;

      m_DatagramDisposalTree.Add(IDatagramDisposal_D3d9_Ptr, RenderDatagramID);  // 放入 pool 中
    }    

    IDatagramDisposal_D3d9_Ptr->Exec(this, IRenderDatagram_Arg, pDEVICE9);
    return true;
  }
};

// 建構出 D3d 環境
class D3d9Context:public cxxlObject
{
//  Smart_Ptr<preD3dEngine> m_preD3dEngine_Ptr;

  Smart_Ptr<WinHandle> WinHandle_Ptr;

  LPDIRECT3D9 pd3d;
  LPDIRECT3DDEVICE9 pdev; // D3D 設備
  IDirect3DSurface9 *pBackBuffer; // 備用緩衝區

  Smart_Ptr<WinHandle> Pick_WinHandle(const Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg)
  {
    if(IWindow_Win32_Arg.isNULL())
      return NULL;
    else 
      return IWindow_Win32_Arg->GetWinHandle();
  }

public:
  // Constructor
  D3d9Context(const Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg)
    :cxxlObject(Spirit_Easy),
    // m_preD3dEngine_Ptr(NULL,this),
    WinHandle_Ptr(Pick_WinHandle(IWindow_Win32_Arg),this)
  {
    pd3d = NULL;
    pdev = NULL;        // D3D 設備
    pBackBuffer = NULL; // 備用緩衝區

    if(IWindow_Win32_Arg.isNULL())
      return;

    if((pd3d=Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
      return;

    // 取得現在的顯示模式
    D3DDISPLAYMODE d3ddm; // D3D 顯示模式
    if(FAILED(pd3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddm)))
      return;

    D3DPRESENT_PARAMETERS d3dpp; // D3D 圖像顯示方法參數結構
    ZeroMemory( &d3dpp, sizeof(D3DPRESENT_PARAMETERS) );

    if(IWindow_Win32_Arg->isFullScreen())
    {
      d3dpp.Windowed = FALSE;    
      d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP;
      d3dpp.BackBufferWidth = d3ddm.Width;           // 螢幕寬度
      d3dpp.BackBufferHeight = d3ddm.Height;         // 螢幕長度
      d3dpp.PresentationInterval=D3DPRESENT_INTERVAL_IMMEDIATE; //立即顯示刷新後的圖像

      d3dpp.BackBufferFormat = d3ddm.Format;         // 色彩深度
      d3dpp.EnableAutoDepthStencil = TRUE;           // 開啟自動深度緩衝
      d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;    // 24 位元深度緩衝區，8 位元遮板緩衝區
      d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; // 允許 back buffer 被鎖定
    }
    else
    {
      d3dpp.Windowed = TRUE;
      d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; 

      d3dpp.BackBufferFormat = d3ddm.Format;      // 色彩深度
      d3dpp.EnableAutoDepthStencil = TRUE;        // 開啟自動深度緩衝
      d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;  // 24 位元深度緩衝區，8 位元遮板緩衝區
      d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; // 允許 back buffer 被鎖定

    }
    d3dpp.BackBufferCount = 1;                     // 有一個後台緩存
    d3dpp.hDeviceWindow = WinHandle_Ptr->Get_hWnd();



    if(
          !FAILED(pd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, WinHandle_Ptr->Get_hWnd(), D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &d3dpp, &pdev)) ||
          !FAILED(pd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, WinHandle_Ptr->Get_hWnd(), D3DCREATE_MIXED_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &d3dpp, &pdev)) ||
          !FAILED(pd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, WinHandle_Ptr->Get_hWnd(), D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &d3dpp, &pdev)) ||
          !FAILED(pd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, WinHandle_Ptr->Get_hWnd(), D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &d3dpp, &pdev)) 
      )
    {
      pdev->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
    }      
  }

  // Destructor
  ~D3d9Context()
  {
    if(pBackBuffer)
      pBackBuffer->Release();

    if(pdev)
      pdev->Release();

    if(pd3d)
      pd3d->Release();
  }

  bool isOK()
  {
    return pBackBuffer != NULL;
  }

  LPDIRECT3DDEVICE9 cxxlFASTCALL GetDEVICE9() const
  {
    return pdev; 
  }

  Smart_Ptr<WinHandle> cxxlFASTCALL GetWinHandle() const
  {
    return WinHandle_Ptr;
  }

};


class CRender_Win32_D3d9:public IRender_Win32_D3d9
{
  // 由使用端提供的初始化函數
  InitD3d9Func m_pInitD3d9Func;

  // 相連結的視窗
  P2Smart_Ptr<IWindow_Win32> IWindow_Win32_Ptr;

  // 負責管理 IDatagramDisposal
  Smart_Ptr<RenderDatagramDisposalCenter> m_RDDC_Ptr;

  // 存放建置 D3D 的相關資料
  Smart_Ptr<D3d9Context> m_D3d9Context_Ptr;


  // 用來比對看窗有沒有變動大小
  LONG  WinWidth;
  LONG  WinHigh;

  D3d9Context * cxxlFASTCALL NewD3d9Context(const Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg)
  {
    D3d9Context *p = new D3d9Context(IWindow_Win32_Arg);
    if(p->isOK())
      return p;
    else
      return NULL;
  }

  // Window_Win32 有變動時自動呼叫通知
  bool cxxlFASTCALL Update()
  {
    CriticalSecHandle AutoCSH(csPreserve);

    m_D3d9Context_Ptr = NewD3d9Context((IWindow_Win32*)IWindow_Win32_Ptr);

    return InitSetup();
  }

  // 保存永續儲存資料
  virtual bool cxxlFASTCALL Save(const CxxlMan::Smart_Ptr<CxxlMan::IcxxlStorage> &IcxxlStorage_Arg, const CxxlMan::UNICODE_String &BackupName)
  {
    return IcxxlStorage_Arg->Save(Smart_Ptr<IRender>(this), BackupName);
  }

  // 取回永續儲存資料，並立即執行，若有錯誤原資料不變
  virtual bool cxxlFASTCALL Load(const CxxlMan::Smart_Ptr<CxxlMan::IcxxlStorage> &IcxxlStorage_Arg, const CxxlMan::UNICODE_String &BackupName)
  {
    Smart_Ptr<IcxxlStorage> tmpStorage_Ptr(new MemStorage);
    if(tmpStorage_Ptr->Save(Smart_Ptr<IRender>(this),L"Tmp") == false)
      return false;

    if(IcxxlStorage_Arg->Load(Smart_Ptr<IRender>(this),BackupName))
    {
      if(Reset())
        return true;

      tmpStorage_Ptr->Load(Smart_Ptr<IRender>(this),L"Tmp");
      Reset();
    }
    
    return false;
  }

  bool cxxlFASTCALL InitSetup()
  {
    if(m_D3d9Context_Ptr != NULL)
    {
      if(m_pInitD3d9Func != NULL)
        return m_pInitD3d9Func(m_D3d9Context_Ptr->GetDEVICE9());

      return true;
    }

    return false;
  }

  virtual bool cxxlFASTCALL Init(InitD3d9Func pInitD3d9Func_Arg)
  {
    CriticalSecHandle AutoCSH(csPreserve);
    m_pInitD3d9Func = pInitD3d9Func_Arg;

    return InitSetup();
  }

  Smart_Ptr<IWindow_Win32> cxxlFASTCALL GetIWindowWin32()
  {
    CriticalSecHandle AutoCSH(csPreserve);

    return IWindow_Win32_Ptr;
  }

  // 以目前指定的 OpenGL 設定再執行一次，成功回覆 true，失敗表示目前指定的視窗模式不適用
  virtual bool cxxlFASTCALL Reset()
  {
    Smart_Ptr<IWindow_Win32> IWindowWin32_Ptr = GetIWindowWin32();

    if(IWindow_Win32_Ptr.isNULL() == false)
      return false;

    return IWindowWin32_Ptr->Reset();
  }

  // 若回覆 false 表示有某個 IRenderDatagram 找不到對應的 IDatagramDisposal
  virtual bool cxxlFASTCALL Draw()
  {

      // 避免執行期間被更換
      csPreserve.Enter();
      Smart_Ptr<cxxlList<ICamera> > Camera_List = GetCameraList();
      Smart_Ptr<D3d9Context> ActiveD3d9Context_Ptr = m_D3d9Context_Ptr;
      csPreserve.Leave();

      if(ActiveD3d9Context_Ptr.isNULL() == false)
      {
        RECT rect; // Client 的範圍

        HWND hWnd = ActiveD3d9Context_Ptr->GetWinHandle()->Get_hWnd();
        GetClientRect(hWnd,&rect);

        if(rect.right != WinWidth || rect.bottom != WinHigh)
        {
          WinWidth = rect.right;
          WinHigh = rect.bottom;

          csPreserve.Enter();
          m_D3d9Context_Ptr = NewD3d9Context((IWindow_Win32*)IWindow_Win32_Ptr);
          ActiveD3d9Context_Ptr = m_D3d9Context_Ptr;
          m_RDDC_Ptr = new RenderDatagramDisposalCenter();
          csPreserve.Leave();
        }

        LPDIRECT3DDEVICE9 pDEVICE9 = ActiveD3d9Context_Ptr->GetDEVICE9();

        // 清除整個畫面的 Viewport
        D3DVIEWPORT9 vp;
        vp.X      = 0;
        vp.Y      = 0,
        vp.Width  = rect.right;
        vp.Height = rect.bottom;
        vp.MinZ   = 0.0f;
        vp.MaxZ   = 1.0f;
        pDEVICE9->SetViewport(&vp); // Set Viewport

        pDEVICE9->Clear( 
          0, NULL, // `清除整個畫面` 
          D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, // `清除顏色跟Z Buffer` 
          D3DCOLOR_RGBA(0, 0, 0, 0), // `設定要把顏色清成黑色`
          1.0f, // `設定要把Z值清為1, 也就是離鏡頭最遠.`
          0 // `設定要把Stencil buffer清為0, 在這沒差.`
        );

        Camera_List->ResetPT(toHead);
        unsigned long Count = Camera_List->GetCount();
        unsigned long i = 0;
        Smart_Ptr<ICamera> Camera_Ptr = (*Camera_List)++;
        for(; i < Count; ++i, Camera_Ptr = (*Camera_List)++)
        {
          float Left, Top, Right, Bottom;
          Camera_Ptr->GetViewport(Left, Top, Right, Bottom);

          // 每個 ICamera 的 Viewport 在視窗中的實際範圍
          //D3DVIEWPORT9 vp;
          vp.X      = rect.right * Left;
          vp.Y      = rect.bottom * Top;
          vp.Width  = rect.right * Right - vp.X;
          vp.Height = rect.bottom * Bottom - vp.Y;
          vp.MinZ   = 0.0f;
          vp.MaxZ   = 1.0f;

          pDEVICE9->SetViewport(&vp); // Set Viewport

          CSmart_Ptr<Matrix4x4> ViewMatrix_cPtr = Camera_Ptr->GetViewMatrix();
          pDEVICE9->SetTransform(D3DTS_VIEW, (D3DXMATRIX*)(Matrix4x4*)ViewMatrix_cPtr); // Set view matrix

          CSmart_Ptr<Matrix4x4> ProjMatrix_cPtr(NULL);
          C3D_FLOAT fh, z_near, z_far;

          if(Camera_Ptr->GetPerspective(fh, z_near, z_far))
          {
            C3D_FLOAT aspect;
            aspect = vp.Height;
            aspect /= vp.Width;
            ProjMatrix_cPtr = MatrixPerspectiveRH_DirectX(fh, aspect, z_near, z_far);
          }
          else
          {
            Camera_Ptr->GetOrtho(fh, z_near, z_far);
            ProjMatrix_cPtr = MatrixOrthoRH_DirectX(fh * vp.Width / vp.Height, fh, z_near, z_far);
          }
          
          pDEVICE9->SetTransform(D3DTS_PROJECTION, (D3DXMATRIX*)(Matrix4x4*)ProjMatrix_cPtr); // Set projection matrix

          Smart_Ptr<IRenderPipe> IRenderPipe_Ptr = Camera_Ptr->GetIRenderPipe();
          if(IRenderPipe_Ptr.isNULL() == false)
          {
            Smart_Ptr<cxxlList<IRenderDatagram> > IRenderDatagram_List = IRenderPipe_Ptr->GetIRenderDatagramList();
            if(IRenderDatagram_List.isNULL() == false)
            {            
              IRenderDatagram_List->ResetPT(toHead);
              for(Smart_Ptr<IRenderDatagram> IRenderDatagram_Ptr = (*IRenderDatagram_List)++;
               IRenderDatagram_Ptr.isNULL() == false;
               IRenderDatagram_Ptr = (*IRenderDatagram_List)++)
              {
                if(m_RDDC_Ptr->Exec(IRenderDatagram_Ptr,pDEVICE9) == false)
                  return false;
              }
            }
          }
        }

        pDEVICE9->Present( NULL, NULL, NULL, NULL );
      }

      return true;
    
  }

  void cxxlFASTCALL SysInit(const Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg)
  {

    IWindow_Win32_Ptr = IWindow_Win32_Arg;
    m_RDDC_Ptr = new RenderDatagramDisposalCenter();
    m_D3d9Context_Ptr = NewD3d9Context(IWindow_Win32_Arg);
  }

protected:

  virtual bool cxxlFASTCALL Ref(CxxlMan::Filter *F)
  {
    if(F != NULL && IRender_Win32_D3d9::Ref(F))
      return true;
    else
      return false;
  }


public:
  // Constructor
  CRender_Win32_D3d9()
    :cxxlObject(Spirit_Urgent),
    cxxlPreserve2(CRender_Win32_D3d9_ClassID),
    IWindow_Win32_Ptr(NULL, this, L"CRender_Win32_D3d9.IWindow_Win32"),
    m_RDDC_Ptr(NULL, this),
    m_D3d9Context_Ptr(NULL, this)
  {
    m_pInitD3d9Func = NULL;

    // IWindow_Win32_Ptr->Attach((IWindowWin32_Observer*)this);
  }

  // Destructor
  virtual ~CRender_Win32_D3d9()
  {
  }


  friend cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID_Arg, void *Arg, ISpirit *spirit);
};



extern "C" CXXL_DLLEXPORT
cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID_Arg, void *Arg, ISpirit *spirit)
{
  if(wcscmp(IRender_Win32_D3d9_ClassID, ClassID_Arg) == 0)
    return new CRender_Win32_D3d9();


  return NULL;
}

extern "C" CXXL_DLLEXPORT
cxxlPreserve2 * cxxlCDECL CxxlMan_Preserve2Factory(const wchar_t *ClassID_Arg)
{
  if(wcscmp(CRender_Win32_D3d9_ClassID, ClassID_Arg) == 0)
    return new CRender_Win32_D3d9();

  return NULL;
}


